package com.dongzili.postgresql.common.mybatisplus;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.mybatis.spring.MyBatisSystemException;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Constructor;
import java.util.*;


@Configuration
@Slf4j
@AutoConfigureAfter(value = {SqlSessionTemplate.class, SpringUtil.class})
public class MybatisPlusMapperConfig{

    public static final Map<String, Class<?>> classMap = new HashMap<>();
    @Resource
    private SqlSessionTemplate sqlSessionTemplate; // 使用SqlSessionTemplate代替SqlSessionFactory

    private static final List<String> basePackages = Collections.singletonList(
            "com.dongzili.postgresql"
    );

    // 根据类名获取 bean name
    private String getBeanName(String className) {
        int index = className.lastIndexOf(".");
        String simpleClassName = index != -1 ? className.substring(index + 1) : className;
        char firstChar = simpleClassName.charAt(0);
        if (firstChar >= 'A' && firstChar <= 'Z') {
            firstChar -= (char) ('A' - 'a');
        }
        return firstChar + simpleClassName.substring(1);
    }


    @PostConstruct
    public void setup() {
        if (sqlSessionTemplate == null) {
            log.error("SqlSessionTemplate 未正确注入，请检查配置。");
            return;
        }

        for (String packageName : basePackages) {
            if (!StringUtils.hasText(packageName)) {
                continue;
            }

            // 扫描数据库实体，根据实体创建对应 mapper、service
            Set<Class<?>> entityClassSet = ClassUtil.scanPackage(packageName);
            if (entityClassSet.isEmpty()) {
                continue;
            }

            for (Class<?> entityClass : entityClassSet) {
                // 只创建带有TableName注解的实体
                TableName tableName = entityClass.getAnnotation(TableName.class);
                if (tableName == null) {
                    continue;
                }

                classMap.put(tableName.value(), entityClass);
                createMapperAndService(entityClass, packageName);
            }
        }
    }

    private void createMapperAndService(Class<?> entityClass, String packageName) {
//        String mapperClassName = packageName + ".mapper" + "." + entityClass.getSimpleName() + "AutoMapper";
//        String serviceImplClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoServiceImpl";
//        String serviceClassName = packageName + ".service" + "." + entityClass.getSimpleName() + "AutoService";
        String mapperClassName = entityClass.getName().replace(entityClass.getSimpleName(),"mapper") + "." + entityClass.getSimpleName() + "AutoMapper";
        String serviceImplClassName = entityClass.getName().replace(entityClass.getSimpleName(),"service") + "." + entityClass.getSimpleName() + "AutoServiceImpl";
        String serviceClassName = entityClass.getName().replace(entityClass.getSimpleName(),"service") + "." + entityClass.getSimpleName() + "AutoService";

        log.debug("开始处理实体类: {}", entityClass.getName());

        /* 创建 mapper */
        try {
            try (DynamicType.Unloaded<?> mapperType = new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(BaseMapper.class, entityClass).build())
                    .name(mapperClassName)
                    .annotateType(AnnotationDescription.Builder.ofType(org.apache.ibatis.annotations.Mapper.class).build())
                    .make()) {

                Class<?> mapperClass = mapperType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                        .getLoaded();

                MapperFactoryBean<?> factoryBean = new MapperFactoryBean<>(mapperClass);
                factoryBean.setSqlSessionTemplate(sqlSessionTemplate);
                sqlSessionTemplate.getConfiguration().addMapper(mapperClass);

                SpringUtil.registerBean(getBeanName(mapperClassName), factoryBean.getObject());
                log.info("自动注册 mybatisplus mapper bean -> 名称:{}, 类名：{}", getBeanName(mapperClassName), mapperClassName);
            }
        } catch (Exception e) {
            if (e instanceof MyBatisSystemException) {
                log.error("MyBatis系统异常，注册Mapper失败: {}", e.getMessage(), e);
            } else if (e instanceof BeanCreationException) {
                log.error("Spring Bean创建异常，注册Mapper失败: {}", e.getMessage(), e);
            } else {
                log.error("自动注册 mybatisplus mapper bean 失败，原因：{}", e.getMessage(), e);
            }
            throw new RuntimeException("自动注册 mybatisplus mapper bean 失败!" + mapperClassName, e);
        }

        /* 创建 service */
        try {
            try (DynamicType.Unloaded<?> serviceType = new ByteBuddy()
                    .makeInterface(TypeDescription.Generic.Builder.parameterizedType(IService.class, entityClass).build())
                    .name(serviceClassName)
                    .make()) {

                Class<?> serviceClass = serviceType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                        .getLoaded();

                List<AnnotationDescription> annotations = new ArrayList<>();
                annotations.add(AnnotationDescription.Builder.ofType(Service.class).build());
                annotations.add(AnnotationDescription.Builder.ofType(Transactional.class).build());

                try (DynamicType.Unloaded<?> serviceImplType = new ByteBuddy()
                        .subclass(TypeDescription.Generic.Builder.parameterizedType(ServiceImpl.class,
                                Class.forName(mapperClassName), entityClass).build())
                        .implement(serviceClass)
                        .name(serviceImplClassName)
                        .annotateType(annotations)
                        .make()) {

                    Class<?> serviceImplClass = serviceImplType.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                            .getLoaded();

                    // 使用构造器创建实例，比newInstance更灵活
                    Constructor<?> constructor = serviceImplClass.getDeclaredConstructor();
                    constructor.setAccessible(true);
                    SpringUtil.registerBean(getBeanName(serviceImplClassName), constructor.newInstance());

                    log.info("自动注册 mybatisplus Service Bean -> 名称:{}, 类名：{}", getBeanName(serviceImplClassName), serviceImplClassName);
                }
            }
        } catch (Exception e) {
            if (e instanceof ClassNotFoundException) {
                log.error("类未找到异常，可能是包名或类名配置错误: {}", e.getMessage(), e);
            } else if (e instanceof NoSuchMethodException) {
                log.error("构造器未找到异常: {}", e.getMessage(), e);
            } else {
                log.error("自动注册 mybatisplus Service Bean 失败，原因：{}", e.getMessage(), e);
            }
            throw new RuntimeException("自动注册 mybatisplus Service Bean 失败!" + serviceImplClassName, e);
        }

        log.debug("成功创建Mapper和Service: {}", entityClass.getName());
    }
}